package cn.zwx.redisson.distributed.lock.example.aspect;

import cn.zwx.redisson.distributed.lock.example.annotation.Lock;
import cn.zwx.redisson.distributed.lock.example.enums.LockModelEnum;
import cn.zwx.redisson.distributed.lock.example.utils.LockUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.TimeUnit;

/**
 * @author zhangwenxue
 */
@Aspect
public class LockAspect {

    private final static Logger logger = LoggerFactory.getLogger(LockAspect.class);

    private RedissonClient redissonClient;

    @Autowired
    public LockAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Pointcut("@annotation(lock)")
    public void controllerAspect(Lock lock) {
    }



    @Around("controllerAspect(lock)")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, Lock lock) throws Throwable {
        String[] keys = lock.keys();
        if (keys.length == 0) {
            throw new RuntimeException("keys不能为空");
        }
        // 等待锁定时间
        long attemptTimeout = lock.attemptTimeout();
        // 锁定最长时间
        long lockWatchdogTimeout = lock.lockWatchdogTimeout();
        // 获取锁类型 如果是自动则需要做判断
        LockModelEnum lockModel = LockUtil.getAutoLockModel(lock, keys);
        if (!lockModel.equals(LockModelEnum.MULTIPLE) && !lockModel.equals(LockModelEnum.RED_LOCK) && keys.length > 1) {
            throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
        }
        logger.info("锁模式->{},等待锁定时间->{}秒.锁定最长时间->{}秒",lockModel.name(),attemptTimeout/1000,lockWatchdogTimeout/1000);
        // 获取锁对象
        RLock rLock = LockUtil.getLock(proceedingJoinPoint,lock,lockModel,redissonClient);
        // 如果没有获取到锁对象 直接执行方法
        if (null == rLock){
          return proceedingJoinPoint.proceed();
        }
        // 否则执行加锁逻辑
       try {
           if (attemptTimeout == -1) {
               //一直等待加锁
               rLock.lock(lockWatchdogTimeout, TimeUnit.MILLISECONDS);
           } else {
               boolean tryLock = rLock.tryLock(attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS);
               if (!tryLock){
                   throw new RuntimeException("获取锁失败");
               }
           }
           // 执行aop方法
           return proceedingJoinPoint.proceed();
       } finally {
          this.processUnlock(rLock);

       }
    }

    private void processUnlock(RLock rLock) {
        // RedissonRedLock 没有实现 isLocked isHeldByCurrentThread
        if (rLock instanceof RedissonRedLock){
            // 释放锁
            rLock.unlock();
        }else {
            // 是否还是锁定状态
            if(rLock.isLocked()){
                // 时候是当前执行线程的锁
                if(rLock.isHeldByCurrentThread()){
                    // 释放锁
                    rLock.unlock();
                }
            }
        }
    }


}
